package munci.core.words;

import static munci.core.Word.IMMEDIATE;

import java.lang.reflect.Field;

import munci.core.Compiler;
import munci.core.Context;
import munci.core.Stack;
import munci.core.Word;
import munci.core.WordImpl;
import munci.read.Token;
import munci.read.Tokens;
import munci.read.Token.Identifier;

public class Basic {
	
	public static void addWords(final Context ctx) {
		ctx.dict()
			.add("words", null, new WordImpl.V0() {
				public void call() {
					System.out.println(ctx.dict().toString());
				}})
			.add("about", null, new WordImpl.V1<CharSequence>() {
				@Override
				public void call(CharSequence name) {
					ctx.dict().find(name.toString(), new WordImpl.R1<Boolean, Word>() {
						public Boolean call(Word word) {
							System.out.println(word);
							return null;
						}});
				}})
			.add("print", null, new WordImpl.V1<Object>() {
				public void call(Object obj) {
					System.out.println(obj);
				}})
			.add("bye", null, new WordImpl.V0() {
				public void call() {
					Runtime.getRuntime().exit(0);
				}})
			.add(".", null, new WordImpl.R0<Token.Identifier>() {
				public Identifier call() {
					Token.Identifier tok = (Token.Identifier) ctx.token();
					ctx.nextToken();
					return tok;
				}})
			.add("$", null, new WordImpl.V0() {
				public void call() {
					ctx.stack().print(System.out);
				}})
			.add("dup", null, new WordImpl.V0() {
				public void call() {
					ctx.stack().dup();
				}})
			.add("swap", null, new WordImpl.V0() {
				public void call() {
					ctx.stack().swap();
				}})
			.add("pop", null, new WordImpl.V0() {
				public void call() {
					ctx.stack().pop();
				}})
			.add("{", null, new WordImpl.R0<Tokens>() {
				public Tokens call() {
					Tokens toks = new Tokens();
					int par = 1;
					for (;;) {
						Token tok = ctx.token();
						ctx.nextToken();
						if (tok.image().equals("}")) {
							par--;
							if (par == 0) break;
						} else if (tok.image().equals("{")) par++;
						toks.add(tok);
					}
					return toks;
				}})
			.add("`", null, new WordImpl.R0<Tokens>() {
				public Tokens call() {
					Tokens toks = new Tokens();
					for (;;) {
						Token tok = ctx.token();
						ctx.nextToken();
						if (tok.image().equals("`")) break;
						toks.add(tok);
					}
					return toks;
				}})
			.add("[", null, new WordImpl.V0(IMMEDIATE) {
				public void call() {
					ctx.setCompiling(false);
				}})
			.add("]", null, new WordImpl.V0() {
				public void call() {
					ctx.setCompiling(true);
				}})
			.add("compile", null, new WordImpl.V2<Tokens, String>() {
				public void call(Tokens toks, String name) {
					try {
						Class<?> cl = new Compiler().compile(toks.toString(), name);
						ctx.dict().scan(cl);
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}})
			.add("(", null, new WordImpl.V0() {
				public void call() {
					ctx.stack().push(END, Object.class);
				}})
			.add(")", null, new WordImpl.R0<Stack>() {
				public Stack call() {
					Stack stack = new Stack();
					for (;;) {
						Class<?> type = ctx.stack().peekClass(0);
						Object top = ctx.stack().pop();
						if (top == END) return stack;
						stack.pushUnder(top, type);
					}
				}})
			.add("exec", null, new WordImpl.V1<Tokens>() {
				public void call(Tokens toks) {
					ctx.interpret(toks.tokens());
				}})
			.add("if", null, new WordImpl.V2<Boolean, Tokens>() {
				public void call(Boolean cond, Tokens toks) {
					if (cond) ctx.interpret(toks.tokens());
				}})
			.add("ifelse", null, new WordImpl.V3<Boolean, Tokens, Tokens>() {
				public void call(Boolean cond, Tokens a, Tokens b) {
					ctx.interpret((cond? a : b).tokens());
				}})
			.add("def", null, new WordImpl.V2<CharSequence, Tokens>() {
				public void call(CharSequence name, final Tokens toks) {
					ctx.dict().add(name.toString(), null, new WordImpl.V0() {
						public void call() {
							ctx.interpret(toks.tokens());
						}});
				}})
			.add("of", null, new WordImpl.V2<Class<?>, CharSequence>() {
				public void call(Class<?> type, CharSequence name) {
					try {
						Field field = type.getField(name.toString());
						ctx.stack().push(field.get(null), field.getType());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}})
			;
	}
	
	private static final Object END = new Object();
}
